package com.youlu.campus.web.common.utils;

import com.alibaba.fastjson.JSON;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;

/**
 * @author sunliang
 * @email 7813496@qq.com
 * @since 2020-07-14 11:00:55
 */

/**
 *
 */
public class HttpClientUtil {

    /**
     * 换行符
     */
//    private static String SLS = System.getProperty("line.separator");
    private static String SLS = " || ";

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtil.class);

    /**
     * 执行一次get请求
     *
     * @param url
     * @param paramMap
     * @param headerMap
     * @param charset
     * @return
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String get(
            String url,
            Map<String, Object> paramMap,
            Map<String, Object> headerMap,
            Integer timeout,
            String charset,
            Integer retryCount,
            Boolean detailLog) throws IOException, URISyntaxException {

        // IF - NULL
        Objects.requireNonNull(url);

        // log
        String requestId = UUID.randomUUID().toString();
        startLog(requestId, url, paramMap, headerMap, detailLog);

        // URL构造器
        URIBuilder uriBuilder = new URIBuilder(url);

        // 创建url
        if (!CollectionUtils.isEmpty(paramMap)) {
            for (Map.Entry<String, Object> item : paramMap.entrySet()) {
                String key = item.getKey();
                Object value = item.getValue();
                if (value == null) {
                    continue;
                }
                uriBuilder.addParameter(key, value.toString());
            }
        }
        HttpGet httpGet = new HttpGet(uriBuilder.build());

        // header
        List<Header> headerList = compartHeaderMap(headerMap);

        // httpclient构造器
        HttpClientBuilder httpClientBuilder = getHttpClientBuilder(headerList, timeout, retryCount);

        // 执行请求
        return doRequest(url, httpClientBuilder, httpGet, charset, detailLog, requestId);

    }

    /**
     * 使用post提交json格式的参数
     *
     * @param url
     * @param paramMap
     * @param headerMap
     * @param timeout
     * @param charset
     * @return
     * @throws IOException
     */
    public static String postJson(
            String url,
            Map<String, Object> paramMap,
            Map<String, Object> headerMap,
            Integer timeout,
            String charset,
            Integer retryCount,
            Boolean detailLog) throws IOException {

        // IF - NULL
        Objects.requireNonNull(url);

        // log
        String requestId = UUID.randomUUID().toString();
        startLog(requestId, url, paramMap, headerMap, detailLog);

        HttpPost httpPost = new HttpPost(url);

        // 设置参数
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(paramMap),charset);
        httpPost.setEntity(stringEntity);

        // 设置header
        List<Header> headerList = compartHeaderMap(headerMap);
        headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));

        // httpclient构造器
        HttpClientBuilder httpClientBuilder = getHttpClientBuilder(headerList, timeout, retryCount);

        // 执行请求
        return doRequest(url, httpClientBuilder, httpPost, charset, detailLog, requestId);
    }

    /**
     * 使用post提交json格式的参数
     *
     * @param url
     * @param paramMap
     * @param headerMap
     * @param timeout
     * @param charset
     * @return
     * @throws IOException
     */
    public static String postForm(
            String url,
            Map<String, Object> paramMap,
            Map<String, Object> headerMap,
            Integer timeout,
            String charset,
            Integer retryCount,
            Boolean detailLog) throws IOException {

        // IF - NULL
        Objects.requireNonNull(url);

        // log
        String requestId = UUID.randomUUID().toString();
        startLog(requestId, url, paramMap, headerMap, detailLog);

        HttpPost httpPost = new HttpPost(url);

        // 设置参数
        List<NameValuePair> paramList = new LinkedList<>();
        if (!CollectionUtils.isEmpty(paramMap)) {
            for (Map.Entry<String, Object> item : paramMap.entrySet()) {
                String key = item.getKey();
                Object value = item.getValue();
                if (value == null) {
                    continue;
                }
                paramList.add(new BasicNameValuePair(key, value.toString()));
            }
        }
        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(paramList, charset);
        httpPost.setEntity(urlEncodedFormEntity);

        // 设置header
        List<Header> headerList = compartHeaderMap(headerMap);

        // httpclient构造器
        HttpClientBuilder httpClientBuilder = getHttpClientBuilder(headerList, timeout, retryCount);

        // 执行请求
        return doRequest(url, httpClientBuilder, httpPost, charset, detailLog, requestId);
    }

    /**
     * 将map形式的请求头转换为list
     *
     * @param headerMap
     * @return
     */
    private static List<Header> compartHeaderMap(Map<String, Object> headerMap) {

        List<Header> headerList = new ArrayList<>();

        // IF - NULL
        if (CollectionUtils.isEmpty(headerMap)) {
            return headerList;
        }

        // FOR - set header
        for (Map.Entry<String, Object> item : headerMap.entrySet()) {
            String key = item.getKey();
            Object value = item.getValue();
            if (value == null) {
                continue;
            }
            headerList.add(new BasicHeader(key, value.toString()));
        }

        return headerList;
    }

    /**
     * 获取默认的httpclient构造器
     *
     * @param headerList
     * @param timeout
     * @param retryCount
     * @return
     */
    private static HttpClientBuilder getHttpClientBuilder(List<Header> headerList, Integer timeout, Integer retryCount) {

        // 设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(timeout)
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .build();

        return HttpClientBuilder
                .create()
                .setDefaultHeaders(headerList)
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, true));

    }

    /**
     * 执行请求
     *
     * @param httpClientBuilder
     * @param httpUriRequest
     * @param charset
     * @param detailLog
     * @return
     * @throws IOException
     */
    private static String doRequest(
            String url,
            HttpClientBuilder httpClientBuilder,
            HttpUriRequest httpUriRequest,
            String charset,
            Boolean detailLog,
            String requestId) throws IOException {

        HttpEntity httpEntity = null;
        try (
                CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
                CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpUriRequest)
        ) {

            httpEntity = closeableHttpResponse.getEntity();
            String result = EntityUtils.toString(httpEntity, charset);
            endLog(requestId, url, result, detailLog);
            return result;
        } finally {
            EntityUtils.consume(httpEntity);
        }
    }

    /**
     * 请求开始日志
     *
     * @param requestId
     * @param url
     * @param paramMap
     * @param headerMap
     * @param detailLog
     */
    private static void startLog(
            String requestId,
            String url,
            Map<String, Object> paramMap,
            Map<String, Object> headerMap,
            Boolean detailLog) {

        if (detailLog) {
            LOGGER.info("{}{}{}url -> {}{}requestId -> {}{}thread -> {}{}header -> {}{}param -> {}",
                    SLS, "================================ request start", SLS,
                    url, SLS,
                    requestId, SLS,
                    Thread.currentThread().getName(), SLS,
                    JSON.toJSONString(headerMap), SLS,
                    JSON.toJSONString(paramMap));
        } else {
            LOGGER.info(url);
        }
    }

    /**
     * 请求结束日志
     *
     * @param uuid
     * @param url
     * @param result
     * @param detailLog
     */
    private static void endLog(
            String uuid,
            String url,
            String result,
            Boolean detailLog) {

        if (detailLog) {
            LOGGER.info("{}{}{}url -> {}{}requestId -> {}{}thread -> {}{}result -> {}",
                    SLS, "================================ request   end", SLS,
                    url, SLS,
                    uuid, SLS,
                    Thread.currentThread().getName(), SLS,
                    result);
        } else {
            LOGGER.info("{}{}{}url -> {}{}requestId -> {}{}end",
                    SLS, "================================ request   end", SLS,
                    url, SLS,
                    uuid, SLS);
        }
    }
}
